package com.hd123.rumba.gwt.widget2.client.dialog;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.json.client.JSONBoolean;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Random;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.FileUpload;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.FormSubmitCompleteEvent;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.Hidden;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FormPanel.SubmitCompleteHandler;
import com.google.gwt.user.client.ui.FormPanel.SubmitHandler;
import com.hd123.rumba.gwt.util.client.GWTUtil;
import com.hd123.rumba.gwt.widget2.client.action.RActionFactory;
import com.hd123.rumba.gwt.widget2.client.grid.RGrid;
import com.hd123.rumba.gwt.widget2.client.grid.RGridClickListener;
import com.hd123.rumba.gwt.widget2.client.grid.RGridColumnDef;
import com.hd123.rumba.gwt.widget2.client.grid.RGridDataProvider;
import com.hd123.rumba.gwt.widget2.client.grid.RGridDrawListener;
import com.hd123.rumba.gwt.widget2.client.grid.renderer.RHyperlinkRendererFactory;
import com.hd123.rumba.gwt.widget2.client.grid.renderer.RImageRendererFactory;
import com.hd123.rumba.gwt.widget2.client.misc.RImageLocation;
import com.hd123.rumba.gwt.widget2.client.misc.RProgressBar;
import com.hd123.rumba.gwt.widget2.client.toolbar.RToolbar;
import com.hd123.rumba.gwt.widget2.client.toolbar.RToolbarButton;
import com.hd123.rumba.gwt.widget2.client.toolbar.RToolbarSeperator;

/**
 * 文件上传对话框
 * 
 * @author LiQi
 * 
 */
public class RUploadDialog extends RDialog {

  /** 样式 */
  private static final String STYLENAME_FORM = "rb-UploadDialog-form";

  /** 图片 */
  private static final String IMAGE_ADDFILE = "images/dialog/file-add.gif";
  private static final String IMAGE_CLEARFILES = "images/dialog/reset.gif";
  private static final String IMAGE_CLEARFILES_DISABLED = "images/dialog/reset-dis.gif";
  private static final String IMAGE_STARTUPLOAD = "images/dialog/upload-start.gif";
  private static final String IMAGE_STARTUPLOAD_DISABLED = "images/dialog/upload-start-dis.gif";
  private static final String IMAGE_STOPUPLOAD = "images/dialog/upload-stop.gif";
  private static final String IMAGE_STOPUPLOAD_DISABLED = "images/dialog/upload-stop-dis.gif";
  private static final String IMAGE_SUCCESS = "images/dialog/upload-success.gif";
  private static final String IMAGE_FAILED = "images/dialog/upload-failed.gif";

  /** 上传请求的URL键值 */
  public static final String UPLOAD_REQUEST_KEY_FILEID = "fileId";
  public static final String UPLOAD_REQUEST_KEY_UPLOAD_FOLDER_ROOT = "uploadFolderRoot";
  public static final String UPLOAD_REQUEST_KEY_MAX_FILESIZE = "maxFileSize";

  /** 上传响应的JSON键值 */
  public static final String UPLOAD_RESPONSE_KEY_SUCCESS = "success"; // Boolean类型，是否成功
  public static final String UPLOAD_RESPONSE_KEY_MESSAGE = "message"; // String类型，错误消息
  public static final String UPLOAD_RESPONSE_KEY_REMOTEFILENAME = "remoteFileName"; // String类型，服务器端文件名

  /** 进度查询请求的URL键值 */
  public static final String PROGRESS_REQUEST_KEY_FILEID = "fileId";

  /** 进度查询响应的JSON键值 */
  public static final String PROGRESS_RESPONSE_KEY_FOUND = "found"; // Boolean类型，是否找到
  public static final String PROGRESS_RESPONSE_KEY_PERCENT = "percent"; // int类型，进度百分比

  /** 进度查询间隔毫秒数 */
  private static final int PROGRESS_QUERY_INTERVAL = 1000;

  /** 基础面板 */
  private VerticalPanel root;

  /** 工具栏 */
  private RToolbar toolbar;

  /** 工具栏按钮 */
  private RToolbarButton clearFilesButton, startUploadButton, stopUploadButton, okButton,
      cancelButton;

  /** 文件表格 */
  private RGrid grid;

  /** 表格列定义 */
  private RGridColumnDef iconCol, fileNameCol, stateCol, actionCol;

  /** 进度条 */
  private RProgressBar progressBar;

  /** 表单容器 */
  private FlowPanel formContainer;

  /** 当前正在添加文件对应的上传表单 */
  private UploadForm currentUploadForm;

  /** 上传进度查询表单 */
  private ProgressQueryForm progressQueryForm;

  /** 单个文件的进度条 */
  private RProgressBar fileProgressBar;

  /** 文件列表 */
  private Files files;

  /** 是否正在上传 */
  private boolean uploading = false;

  /**
   * 正在上传文件列表，点击上传按钮时状态处于STATE_NOTUPLOAD的文件，在上传过程中保持不变，
   * 所有文件上传结束后清空
   */
  private Files uploadingFiles;

  /** 默认的上传文件夹根 */
  private static final String DEFAULT_UPLOAD_FOLDER_ROOT = "c:/temp";

  /** 上传文件夹的根 */
  private String uploadFolderRoot = DEFAULT_UPLOAD_FOLDER_ROOT;

  /** 回调 */
  private Callback callback;

  /** 允许上传的最大的文件字节数 */
  private long maxFileSize = -1;

  public RUploadDialog() {
    super(false, true);
    files = new Files();
    setCaptionText("上传文件");

    root = new VerticalPanel();
    setWidget(root);
    buildToolbar();

    progressBar = new RProgressBar();
    progressBar.setHeight("23px");
    root.add(progressBar);

    buildGrid();

    formContainer = new FlowPanel();
    root.add(formContainer);
    progressQueryForm = new ProgressQueryForm();
    fileProgressBar = new RProgressBar();
    fileProgressBar.setWidth("100px");
    fileProgressBar.setHeight("17px");
  }

  /**
   * @param callback
   *          回调
   */
  public RUploadDialog(Callback callback) {
    this();
    setCallback(callback);
  }

  /**
   * @param callback
   *          回调
   * @param uploadFolerRoot
   *          上传文件夹的根，默认为{@link #DEFAULT_UPLOAD_FOLDER_ROOT}。
   *          <p>
   *          取值：
   *          <p>
   *          <li>绝对路径，即服务器上的物理文件夹路径。
   *          <li>相对路径，web应用下的相对路径，例如当web应用的url为
   * 
   *          <pre>
   * http://localhost/my-web
   * </pre>
   * 
   *          并且指定了相对路径为
   * 
   *          <pre>
   * static/rumba/upload
   * </pre>
   * 
   *          则最终的上传文件夹的根为
   * 
   *          <pre>
   * http://localhost/my-web/static/rumba/upload
   * </pre>
   * 
   *          如果传入null，则会自动改为{@link #DEFAULT_UPLOAD_FOLDER_ROOT}
   */
  public RUploadDialog(Callback callback, String uploadFolerRoot) {
    this();
    setCallback(callback);
    setUploadFolderRoot(uploadFolerRoot);
  }

  /**
   * 构造工具栏
   * 
   */
  private void buildToolbar() {
    toolbar = new RToolbar();
    root.add(toolbar);

    // 添加按钮
    RToolbarButton addFileButton = new RToolbarButton("添加");
    addFileButton.setIcon(new RImageLocation(IMAGE_ADDFILE));
    addFileButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        addFile();
      }
    });
    toolbar.add(addFileButton);

    // 清空按钮
    clearFilesButton = new RToolbarButton("清空");
    clearFilesButton.setIcon(new RImageLocation(IMAGE_CLEARFILES));
    clearFilesButton.setDisabledIcon(new RImageLocation(IMAGE_CLEARFILES_DISABLED));
    clearFilesButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        clearFiles();
      }
    });
    toolbar.add(clearFilesButton);

    toolbar.add(new RToolbarSeperator());

    // 开始上传按钮
    startUploadButton = new RToolbarButton("上传");
    startUploadButton.setIcon(new RImageLocation(IMAGE_STARTUPLOAD));
    startUploadButton.setDisabledIcon(new RImageLocation(IMAGE_STARTUPLOAD_DISABLED));
    startUploadButton.setEnabled(false);
    startUploadButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        startUpload();
      }
    });
    toolbar.add(startUploadButton);

    // 停止上传按钮
    stopUploadButton = new RToolbarButton("停止");
    stopUploadButton.setIcon(new RImageLocation(IMAGE_STOPUPLOAD));
    stopUploadButton.setDisabledIcon(new RImageLocation(IMAGE_STOPUPLOAD_DISABLED));
    stopUploadButton.setEnabled(false);
    stopUploadButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        stopUpload();
      }
    });
    toolbar.add(stopUploadButton);

    // 确定按钮
    okButton = new RToolbarButton(RActionFactory.makeOkAction(new ClickHandler() {
      public void onClick(ClickEvent event) {
        RUploadDialog.this.hide();
        if (callback != null)
          callback.onClosed(true, getUploadedFiles());
      }
    }));
    toolbar.add(okButton, RToolbar.Alignment.right);

    // 取消按钮
    cancelButton = new RToolbarButton(RActionFactory.makeCancelAction(new ClickHandler() {
      public void onClick(ClickEvent event) {
        RUploadDialog.this.hide();
        if (callback != null)
          callback.onClosed(false, null);
      }
    }));
    toolbar.add(cancelButton, RToolbar.Alignment.right);
  }

  /**
   * 构造表格
   * 
   */
  private void buildGrid() {
    grid = new RGrid();
    grid.setProvider(new GridProvider());
    GridListener gridListener = new GridListener();
    grid.addRGridClickListener(gridListener);
    grid.addRGridDrawListener(gridListener);

    iconCol = new RGridColumnDef();
    iconCol.setWidth("20px");
    iconCol.setHorizontalAlign(HasAlignment.ALIGN_CENTER);
    iconCol.setRendererFactory(new RImageRendererFactory());
    grid.addColumnDef(iconCol);

    fileNameCol = new RGridColumnDef("文件名");
    fileNameCol.setWidth("280px");
    grid.addColumnDef(fileNameCol);

    stateCol = new RGridColumnDef("状态");
    stateCol.setWidth("120px");
    grid.addColumnDef(stateCol);

    actionCol = new RGridColumnDef();
    actionCol.setWidth("35px");
    actionCol.setHorizontalAlign(HasAlignment.ALIGN_CENTER);
    actionCol.setRendererFactory(new RHyperlinkRendererFactory(false));
    grid.addColumnDef(actionCol);

    grid.rebuild();
    root.add(grid);
  }

  /**
   * 添加文件
   * 
   */
  private void addFile() {
    if (currentUploadForm == null)
      currentUploadForm = new UploadForm();
    currentUploadForm.getUploadInput().click();
  }

  /**
   * 清除所有文件
   * 
   */
  public void clearFiles() {
    files.clear();
    grid.refresh();
    progressBar.reset();
    startUploadButton.setEnabled(false);
  }

  /**
   * 开始上传，点击上传按钮时执行
   * 
   */
  private void startUpload() {
    uploadingFiles = files.getByState(FileInfo.STATE_NOTUPLOAD);
    if (uploadingFiles.size() == 0)
      return;

    uploading = true;
    clearFilesButton.setEnabled(false);
    startUploadButton.setEnabled(false);
    stopUploadButton.setEnabled(true);
    okButton.setEnabled(false);
    cancelButton.setEnabled(false);
    progressBar.setMin(0);
    progressBar.setMax(uploadingFiles.size());
    grid.refresh();

    uploadingFiles.getFile(0).getUploadForm().submit();
  }

  /**
   * 停止上传，点击停止按钮时执行
   * 
   */
  private void stopUpload() {
    uploading = false;
    uploadingFiles.clear();
    clearFilesButton.setEnabled(true);
    startUploadButton.setEnabled(files.getByState(FileInfo.STATE_NOTUPLOAD).size() > 0);
    stopUploadButton.setEnabled(false);
    okButton.setEnabled(true);
    cancelButton.setEnabled(true);
    progressBar.reset();
    grid.refresh();

    FileInfo fileInfo = files.getUploading();
    if (fileInfo != null) {
      fileInfo.setState(FileInfo.STATE_CANCELED);
      grid.refreshRow(fileInfo.getRow());
    }
  }

  /**
   * 开始一个文件的上传
   * 
   * @param fileInfo
   */
  private void startUpload(FileInfo fileInfo) {
    if (fileInfo == null)
      return;

    fileInfo.setState(FileInfo.STATE_UPLOADING);
    fileProgressBar.reset();
    grid.refreshRow(fileInfo.getRow());
    progressQueryForm.setFileInfo(fileInfo);
    progressQueryForm.submit();
  }

  /**
   * 完成一个文件的上传
   * 
   * @param fileInfo
   * @param result
   */
  private void finishUpload(FileInfo fileInfo, JSONObject result) {
    if (fileInfo == null)
      return;

    // 若用户点击了停止按钮则放弃
    if (!uploading)
      return;

    // 解析JSON返回值并修改文件状态
    boolean success = ((JSONBoolean) result.get(UPLOAD_RESPONSE_KEY_SUCCESS)).booleanValue();
    fileInfo.setState(success ? FileInfo.STATE_UPLOADED : FileInfo.STATE_UPLOADFAILED);

    JSONValue message = result.get(UPLOAD_RESPONSE_KEY_MESSAGE);
    fileInfo.setMessage(message == null ? null : ((JSONString) message).stringValue());

    JSONValue remoteFileName = result.get(UPLOAD_RESPONSE_KEY_REMOTEFILENAME);
    fileInfo.setRemoteName(remoteFileName == null ? null : ((JSONString) remoteFileName)
        .stringValue());

    // 刷新界面
    grid.refreshRow(fileInfo.getRow());
    progressBar.step();

    // 执行下一个文件上传
    FormPanel nextForm = uploadingFiles.getNextUploadForm(fileInfo.getUploadForm());
    if (nextForm != null)
      nextForm.submit();
    else
      finishUpload();
  }

  /**
   * 完成上传
   * 
   */
  private void finishUpload() {
    uploading = false;
    clearFilesButton.setEnabled(true);
    startUploadButton.setEnabled(files.getByState(FileInfo.STATE_NOTUPLOAD).size() > 0);
    stopUploadButton.setEnabled(false);
    okButton.setEnabled(true);
    cancelButton.setEnabled(true);
    grid.refresh();
  }

  /**
   * 上传文件夹的根，默认为{@link #DEFAULT_UPLOAD_FOLDER_ROOT}。
   * <p>
   * 取值：
   * <p>
   * <li>绝对路径，即服务器上的物理文件夹路径。
   * <li>相对路径，web应用下的相对路径，例如当web应用的url为
   * 
   * <pre>
   * http://localhost/my-web
   * </pre>
   * 
   * 并且指定了相对路径为
   * 
   * <pre>
   * static/rumba/upload
   * </pre>
   * 
   * 则最终的上传文件夹的根为
   * 
   * <pre>
   * http://localhost/my-web/static/rumba/upload
   * </pre>
   * 
   * @return
   */
  public String getUploadFolderRoot() {
    return uploadFolderRoot;
  }

  /**
   * 上传文件夹的根，默认为{@link #DEFAULT_UPLOAD_FOLDER_ROOT}。
   * <p>
   * 取值：
   * <p>
   * <li>绝对路径，即服务器上的物理文件夹路径。
   * <li>相对路径，web应用下的相对路径，例如当web应用的url为
   * 
   * <pre>
   * http://localhost/my-web
   * </pre>
   * 
   * 并且指定了相对路径为
   * 
   * <pre>
   * static/rumba/upload
   * </pre>
   * 
   * 则最终的上传文件夹的根为
   * 
   * <pre>
   * http://localhost/my-web/static/rumba/upload
   * </pre>
   * 
   * @param uploadFolderRoot
   *          如果传入null，则会自动改为{@link #DEFAULT_UPLOAD_FOLDER_ROOT}
   */
  public void setUploadFolderRoot(String uploadFolderRoot) {
    this.uploadFolderRoot = uploadFolderRoot == null ? DEFAULT_UPLOAD_FOLDER_ROOT
        : uploadFolderRoot;
  }

  /**
   * 已上传文件的列表。 文件名从uploadFolderRoot开始，不包含uploadFolderRoot。
   * 
   * @return
   */
  public List getUploadedFiles() {
    return files.getByState(FileInfo.STATE_UPLOADED).getRemoteNames();
  }

  /**
   * 已上传文件的列表。 文件名包含uploadFolderRoot。
   * 
   * @return
   */
  public List getUploadedFilesWithRoot() {
    return files.getByState(FileInfo.STATE_UPLOADED).getRemoteNamesWithRoot();
  }

  /**
   * 回调
   * 
   * @return
   */
  public Callback getCallback() {
    return callback;
  }

  /**
   * 设置回调
   * 
   * @param callback
   */
  public void setCallback(Callback callback) {
    this.callback = callback;
  }

  /**
   * 允许上传的最大的文件字节数，默认为-1，表示不限制
   * 
   * @return
   */
  public long getMaxFileSize() {
    return maxFileSize;
  }

  /**
   * 设置允许上传的最大的文件字节数，默认为-1，表示不限制
   * 
   * @param maxFileSize
   */
  public void setMaxFileSize(long maxFileSize) {
    this.maxFileSize = maxFileSize;
  }

  /**
   * 把解析服务端返回的字符串解析为JSON对象。
   * <p>
   * 其实在IE中只要这么写就可以了：
   * 
   * <pre>
   * JSONParser.parse(text)
   * </pre>
   * 
   * 但在Chrome中，从{@link FormSubmitCompleteEvent#getResults()}
   * 得到的字符串内容为：
   * 
   * <pre>
   * &lt;pre style=&quot;word-wrap: break-word; white-space: pre-wrap;&quot;&gt;真正的JSON字符串&lt;/pre&gt;
   * </pre>
   * 
   * 也就是说真正的JSON字符串被一个
   * 
   * <pre>
   * 包住了，如果直接把这个字符串给JSONParser解析会无法解析，所以需要用HTML
   * 控件把多余的字符过滤掉：
   * 
   * <pre>
   * JSONParser.parse(new HTML(html).getText());
   * </pre>
   * 
   * @param html
   * @return
   */
  private JSONObject parseJSON(String html) {
    return (JSONObject) JSONParser.parse(new HTML(html).getText());
  }

  /**
   * 上传表单
   * 
   * @author LiQi
   * 
   */
  private class UploadForm extends FormPanel implements SubmitHandler, SubmitCompleteHandler {

    /** 上传框 */
    private FileUploadInput uploadInput;

    public UploadForm() {
      super();
      setStyleName(STYLENAME_FORM);
      setMethod(FormPanel.METHOD_POST);
      setEncoding(FormPanel.ENCODING_MULTIPART);
      addSubmitHandler(this);
      addSubmitCompleteHandler(this);
      formContainer.add(this);

      FlowPanel root = new FlowPanel();
      setWidget(root);

      uploadInput = new FileUploadInput();
      uploadInput.setName("uploadFile");
      root.add(uploadInput);
    }

    public void onSubmit(SubmitEvent event) {
      FileInfo fileInfo = files.getByUploadForm(this);
      setAction(GWTUtil.getWebAppBaseURL() + "upload?" + UPLOAD_REQUEST_KEY_FILEID + "="
          + fileInfo.getId() + "&" + UPLOAD_REQUEST_KEY_UPLOAD_FOLDER_ROOT + "=" + uploadFolderRoot
          + "&" + UPLOAD_REQUEST_KEY_MAX_FILESIZE + "=" + maxFileSize);
      startUpload(fileInfo);
    }

    public void onSubmitComplete(SubmitCompleteEvent event) {
      finishUpload(files.getByUploadForm(this), parseJSON(event.getResults()));
    }

    /**
     * 上传框
     * 
     * @return
     */
    public FileUploadInput getUploadInput() {
      return uploadInput;
    }

  }

  /**
   * 自定义的FileUpload，增加onChange事件和click方法
   * 
   * @author LiQi
   * 
   */
  private class FileUploadInput extends FileUpload {

    public FileUploadInput() {
      super();
      sinkEvents(Event.ONCHANGE);
    }

    public void onBrowserEvent(Event event) {
      super.onBrowserEvent(event);
      if (DOM.eventGetType(event) == Event.ONCHANGE)
        onChange();
    }

    private void onChange() {
      if (files.getByLocalFullName(getFilename()) != null) {
        RMsgBox.show("文件 <b>" + getFilename() + "</b> 已经被添加过了，不能再次添加。");
        currentUploadForm = null;
        return;
      }

      FileInfo fileInfo = new FileInfo();
      fileInfo.setLocalFullName(getFilename());
      fileInfo.setUploadForm(currentUploadForm);
      currentUploadForm = null;
      fileInfo.setRow(files.size() + grid.getHeaderRowCount());
      files.add(fileInfo);
      grid.refresh();
      startUploadButton.setEnabled(!uploading);
    }

    /**
     * 模拟点击
     * 
     */
    public void click() {
      click(getElement());
    }

    private native void click(Element element)
    /*-{
      element.click();
    }-*/;
  }

  /**
   * 进度查询表单
   * 
   * @author LiQi
   * 
   */
  private class ProgressQueryForm extends FormPanel implements SubmitHandler, SubmitCompleteHandler {

    /** 对应的文件 */
    private FileInfo fileInfo;

    /** 定时器，用于触发下一次查询 */
    private Timer timer;

    private Hidden fileId;

    public ProgressQueryForm() {
      super();
      setStyleName(STYLENAME_FORM);
      setMethod(FormPanel.METHOD_GET);
      setAction(GWTUtil.getWebAppBaseURL() + "upload");
      setEncoding(FormPanel.ENCODING_URLENCODED);
      addSubmitHandler(this);
      addSubmitCompleteHandler(this);
      formContainer.add(this);

      fileId = new Hidden(PROGRESS_REQUEST_KEY_FILEID);
      add(fileId);

      timer = new Timer() {
        public void run() {
          ProgressQueryForm.this.submit();
        }
      };
    }

    public void onSubmit(SubmitEvent event) {
      fileId.setValue(fileInfo.getId());
    }

    public void onSubmitComplete(SubmitCompleteEvent event) {
      if (!fileInfo.getState().equals(FileInfo.STATE_UPLOADING))
        return;

      // 解析返回结果
      JSONObject result = parseJSON(event.getResults());
      boolean found = ((JSONBoolean) result.get(PROGRESS_RESPONSE_KEY_FOUND)).booleanValue();
      if (found) {
        int progressPercent = new Long(Math.round(((JSONNumber) result
            .get(PROGRESS_RESPONSE_KEY_PERCENT)).doubleValue())).intValue();

        fileProgressBar.setPosition(progressPercent);
        grid.refreshCell(fileInfo.getRow(), stateCol.getIndex());
      }

      // 延时后执行下一次查询
      timer.schedule(PROGRESS_QUERY_INTERVAL);
    }

    public FileInfo getFileInfo() {
      return fileInfo;
    }

    public void setFileInfo(FileInfo fileInfo) {
      this.fileInfo = fileInfo;
    }

  }

  /**
   * 表格数据提供者
   * 
   * @author LiQi
   * 
   */
  private class GridProvider implements RGridDataProvider {

    public int getRowCount() {
      return Math.max(10, files.size());
    }

    public Object getData(int row, int col) {
      if (row >= files.size())
        return "";

      FileInfo fileInfo = files.getFile(row);
      if (col == iconCol.getIndex())
        return fileInfo.getIconUrl();
      else if (col == fileNameCol.getIndex()) {
        return fileInfo.getName();
      } else if (col == stateCol.getIndex()) {
        return fileInfo.getState()
            + (FileInfo.STATE_UPLOADFAILED.equals(fileInfo.getState()) ? "."
                + fileInfo.getMessage() : "");
      } else if (col == actionCol.getIndex()) {
        return uploading ? "" : "删除";
      }

      return "";
    }
  }

  /**
   * 表格事件处理
   * 
   * @author LiQi
   * 
   */
  private class GridListener implements RGridClickListener, RGridDrawListener {

    public void onClick(RGrid grid, RGridColumnDef colDef, int row, int col, Widget sender) {
      if (row < grid.getHeaderRowCount()
          || row >= grid.getRowCount() - grid.getFooterRowCount() - 1)
        return;

      // 正在上传时不允许删除
      if (col != actionCol.getIndex() || uploading)
        return;

      files.remove(row - grid.getHeaderRowCount());
      grid.refresh();
      startUploadButton.setEnabled(files.getByState(FileInfo.STATE_NOTUPLOAD).size() > 0);
    }

    public void onBuildCell(RGrid grid, RGridColumnDef colDef, int row, int col) {
    }

    public void onRefreshCell(RGrid grid, RGridColumnDef colDef, int row, int col,
        Object providerData) {
      if (row < grid.getHeaderRowCount()
          || row >= grid.getRowCount() - grid.getFooterRowCount() - 1)
        return;
      if (col != stateCol.getIndex() || row - grid.getHeaderRowCount() > files.size() - 1)
        return;

      FileInfo fileInfo = files.getFile(row - grid.getHeaderRowCount());
      if (!fileInfo.getState().equals(FileInfo.STATE_UPLOADING)) {
        grid.setText(row, col, (String) providerData);
        grid.getCellFormatter().setHorizontalAlignment(row, col, HasAlignment.ALIGN_LEFT);
      } else if (grid.getWidget(row, col) == null) {
        grid.setWidget(row, col, fileProgressBar);
        grid.getCellFormatter().setHorizontalAlignment(row, col, HasAlignment.ALIGN_CENTER);
      }
    }

    public void afterRefresh(RGrid grid) {
    }
  }

  public interface Callback {

    /**
     * 当对话框关闭时触发
     * 
     * @param ok
     *          是否点击了OK按钮
     * @param uploadedFiles
     *          已上传的文件，参见{@link RUploadDialog#getUploadedFiles()}
     *          。点击取消按钮时为null。
     */
    void onClosed(boolean ok, List uploadedFiles);
  }

  /**
   * 上传文件列表
   * 
   * @author LiQi
   * 
   */
  private class Files extends ArrayList {

    private static final long serialVersionUID = 1L;

    public FileInfo getFile(int index) {
      return (FileInfo) get(index);
    }

    /**
     * 根据localFullName查找
     * 
     * @param localFullName
     * @return 未找到则返回null
     */
    public FileInfo getByLocalFullName(String localFullName) {
      if (localFullName == null)
        return null;
      for (Iterator i = iterator(); i.hasNext();) {
        FileInfo fileInfo = (FileInfo) i.next();
        if (localFullName.equals(fileInfo.getLocalFullName()))
          return fileInfo;
      }
      return null;
    }

    /**
     * 根据上传表单查找
     * 
     * @return
     */
    public FileInfo getByUploadForm(FormPanel form) {
      if (form == null)
        return null;
      for (Iterator i = iterator(); i.hasNext();) {
        FileInfo fileInfo = (FileInfo) i.next();
        if (form.equals(fileInfo.getUploadForm()))
          return fileInfo;
      }
      return null;
    }

    /**
     * 根据状态查找
     * 
     * @return
     */
    public Files getByState(String state) {
      Files result = new Files();
      for (Iterator i = iterator(); i.hasNext();) {
        FileInfo fileInfo = (FileInfo) i.next();
        if (fileInfo.getState().equals(state))
          result.add(fileInfo);
      }
      return result;
    }

    /**
     * 查找正在上传的文件，没找到则返回null
     * 
     * @return
     */
    public FileInfo getUploading() {
      Files uploadings = getByState(FileInfo.STATE_UPLOADING);
      return uploadings.size() == 0 ? null : uploadings.getFile(0);
    }

    /**
     * 按顺序查找指定表单的下一个表单，如果指定表单已经是最后一个则返回null
     * 
     * @return
     */
    public FormPanel getNextUploadForm(FormPanel form) {
      if (form == null)
        return null;
      for (int i = 0; i < size(); i++) {
        FileInfo fileInfo = getFile(i);
        if (form.equals(fileInfo.getUploadForm()))
          return i == size() - 1 ? null : getFile(i + 1).getUploadForm();
      }
      return null;
    }

    /**
     * 获得remoteName的列表
     * 
     * @return
     */
    public List getRemoteNames() {
      List list = new ArrayList();
      for (Iterator i = iterator(); i.hasNext();)
        list.add(((FileInfo) i.next()).getRemoteName());
      return list;
    }

    /**
     * 获得remoteNameWithRoot的列表
     * 
     * @return
     */
    public List getRemoteNamesWithRoot() {
      List list = new ArrayList();
      for (Iterator i = iterator(); i.hasNext();)
        list.add(((FileInfo) i.next()).getRemoteNameWithRoot());
      return list;
    }
  }

  /**
   * 上传文件的信息
   * 
   * @author LiQi
   * 
   */
  private class FileInfo {

    public static final String STATE_NOTUPLOAD = "未上传";
    public static final String STATE_UPLOADING = "正在上传";
    public static final String STATE_UPLOADED = "上传成功";
    public static final String STATE_UPLOADFAILED = "上传失败";
    public static final String STATE_CANCELED = "用户取消";

    /** 文件的唯一标识 */
    private String id;

    /** 文件名，不包含路径 */
    private String name;

    /** 本地文件全名，包含路径 */
    private String localFullName;

    /**
     * 已上传到服务端的文件名，文件名从uploadFolderRoot开始
     */
    private String remoteName;

    /** 上传表单 */
    private FormPanel uploadForm;

    /** 当前状态 */
    private String state = STATE_NOTUPLOAD;

    /** 服务端消息 */
    private String message;

    /** 在表格中的行 */
    private int row = -1;

    public String getId() {
      return id;
    }

    public String getName() {
      return name;
    }

    public String getLocalFullName() {
      return localFullName;
    }

    public void setLocalFullName(String localFullName) {
      this.localFullName = localFullName;
      name = extractFileName(localFullName);
      id = localFullName + "_" + Random.nextInt();
    }

    public String getRemoteName() {
      return remoteName;
    }

    public void setRemoteName(String remoteName) {
      this.remoteName = remoteName;
    }

    public FormPanel getUploadForm() {
      return uploadForm;
    }

    public void setUploadForm(FormPanel uploadForm) {
      this.uploadForm = uploadForm;
    }

    public String getState() {
      return state;
    }

    public void setState(String state) {
      this.state = state;
    }

    public String getMessage() {
      return message;
    }

    public void setMessage(String message) {
      this.message = message;
    }

    public int getRow() {
      return row;
    }

    public void setRow(int row) {
      this.row = row;
    }

    /**
     * 获得包含uploadFolderRoot的服务端文件名
     * 
     */
    public String getRemoteNameWithRoot() {
      return uploadFolderRoot + "/" + remoteName;
    }

    /**
     * 根据当前状态返回图标地址，不需要显示图标时返回null
     * 
     * @return
     */
    public String getIconUrl() {
      if (state == null)
        return null;
      else if (state.equals(STATE_UPLOADED))
        return IMAGE_SUCCESS;
      else if (state.equals(STATE_CANCELED) || state.equals(STATE_UPLOADFAILED))
        return IMAGE_FAILED;
      else
        return null;
    }

    /**
     * 从包含路径的文件名中剥离出文件名
     * 
     * @param fileNameWithPath
     * @return
     */
    private String extractFileName(String fileNameWithPath) {
      int position = fileNameWithPath.lastIndexOf("/");
      if (position == -1)
        position = fileNameWithPath.lastIndexOf("\\");

      if (position == -1)
        return fileNameWithPath;
      else
        return fileNameWithPath.substring(position + 1, fileNameWithPath.length());
    }
  }

}
